package com.android.wellcherish.workclock.database;

import android.content.Context;
import android.util.Log;
import com.android.wellcherish.workclock.bean.db.Record;
import com.android.wellcherish.workclock.dao.RecordDao;
import com.android.wellcherish.workclock.util.CommonUtil;
import com.android.wellcherish.workclock.util.TimeUtil;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author 张文琛
 * @date 2019/12/29 22:33
 *
 * 处理数据库操作的Manager，采用锁操作，保证数据一致性，异步线程调用应该使用接口回调
 */
public class DbTaskManager {
    private static final String TAG = DbTaskManager.class.getSimpleName();
    /**
     * 执行异步操作的线程池
     * */
    private ExecutorService executor;
    /**
     * 数据库实例
     * */
    private AppDatabase database;

    /**
     * 上下文环境
     * */
    private Context context;

    public DbTaskManager(Context context, AppDatabase database) {
        // 创建线程数量为5的固定线程池
        executor = Executors.newFixedThreadPool(5);
        this.context = context;
        if(database != null) {
            this.database = database;
        } else {
            this.database = AppDatabase.getInstance(context);
        }
    }

    /**
     * 取消所有正在执行的任务，并终结线程池
     * synchronized是个耗时操作，影响范围应该尽量小
     * */
    public synchronized void cancel() {
        executor.shutdown();
    }

    public boolean createOrUpdateRecord(long id) {
        database.beginTransaction();
        try {
            List<Record> records = database.recordDao().queryAll();
            boolean isIn = false;
            Record record = null;
            for(Record record2 : records) {
                if(record2.getId() == id) {
                    record = record2;
                    break;
                }
            }
            long newId;
            if(record != null) {
                newId = database.recordDao().update(record);
            } else {
                newId = database.recordDao().insert(record);
            }
            database.setTransactionSuccessful();
            return newId > 0;
        } finally {
            database.endTransaction();
        }
    }

    /**
     * 增删查改操作：增
     * */
    public boolean insert(Record record) {
        return runInsert(record);
    }
    /**
     * 增删查改操作：删
     * */
    public boolean delete(Record record) {
        return runDelete(record);
    }

    /**
     * 增删查改操作：查
     * */
    public List<Record> queryAll() {
        return runQueryAll();
    }
    /**
     * 增删查改操作：改
     * */
    public boolean update(Record record) {
        return runUpdate(record);
    }

    /**
     * 实际的插入操作
     * */
    private boolean runInsert(final Record record) {
        Callable<Boolean> callable = new Callable<Boolean>() {
            @Override public Boolean call() throws Exception {
                return database.recordDao().insert(record) > -1;
            }
        };

        boolean result;
        try {
            Future<Boolean> task = executor.submit(callable);
            result = task.get();
        } catch (Exception e) {
            Log.d(TAG, "执行插入操作时，发生错误", e);
            result = false;
        }

        return result;
    }

    /**
     * 实际的删除操作
     * */
    private boolean runDelete(final Record record) {
        Callable<Boolean> callable = new Callable<Boolean>() {
            @Override public Boolean call() throws Exception {
                return database.recordDao().delete(record) > 0;
            }
        };

        boolean result;
        try {
            Future<Boolean> task = executor.submit(callable);
            result = task.get();
        } catch (Exception e) {
            Log.d(TAG, "执行插入操作时，发生错误", e);
            result = false;
        }

        return result;
    }

    /**
     * 实际的查询操作，查询所有记录
     * */
    private List<Record> runQueryAll() {
        Callable<List<Record>> callable = new Callable<List<Record>>() {
            @Override public List<Record> call() throws Exception {
                return database.recordDao().queryAll();
            }
        };

        List<Record> result;
        try {
            Future<List<Record>> task = executor.submit(callable);
            result = task.get();
        } catch (Exception e) {
            Log.d(TAG, "执行插入操作时，发生错误", e);
            result = new ArrayList<>(0);
        }

        return result;
    }

    /**
     * 得到某天的所有打卡记录
     *
     * @param instant 可以构建出某个日期的时间戳
     *
     * @return 某天的所有打卡记录的列表
     * */
    public List<Record> querySomedayRecords(Instant instant) {
        // ZonedDateTime 包含着最全的时间信息
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of(TimeUtil.ZONE_ID));
        // 获得查询范围的起止事件，单位是秒
        final long startInclude = TimeUtil.getTimeInstant(zonedDateTime);
        // 天数加 1 天
        final long endExclude = TimeUtil.getTimeInstant(zonedDateTime.plus(1, ChronoUnit.DAYS));

        Callable<List<Record>> callable = new Callable<List<Record>>() {
            @Override public List<Record> call() throws Exception {
                if(startInclude == -1 || endExclude == -1) {
                    return null;
                }
                return database.recordDao().queryByTimeInterval(startInclude, endExclude);
            }
        };

        List<Record> result;
        try {
            Future<List<Record>> task = executor.submit(callable);
            result = task.get();
        } catch (Exception e) {
            Log.d(TAG, "执行插入操作时，发生错误", e);
            result = new ArrayList<>(0);
        }

        return result;
    }

    /**
     * 实际的更新操作
     * */
    private boolean runUpdate(final Record record) {
        Callable<Boolean> callable = new Callable<Boolean>() {
            @Override public Boolean call() throws Exception {
                return database.recordDao().update(record) > 0;
            }
        };

        boolean result;
        try {
            Future<Boolean> task = executor.submit(callable);
            result = task.get();
        } catch (Exception e) {
            Log.d(TAG, "执行插入操作时，发生错误", e);
            result = false;
        }

        return result;
    }
}
